package net.lb.io.json.field;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import net.lb.baseutil.collection.ObjectCollect;
import net.lb.baseutil.collection.StringCache;
import net.lb.io.json.JsonArray;
import net.lb.io.json.JsonObject;
import net.lb.io.json.annotation.Rename;
import sun.misc.Unsafe;

@SuppressWarnings("restriction")
public abstract class JsonField
{
    protected Field         field;
    protected Class<?>      rootType;
    protected long          offset    = 0;
    protected String        name;
    protected String        jsonKey;
    protected static Unsafe unsafe;
    protected int           dimension = 0;
    static
    {
        try
        {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
    /**
     * 构造jsonfield。如果field是一个数组，则会分析出数组的维度和实际类型
     * 
     * @param field
     */
    public JsonField(Field field)
    {
        offset = unsafe.objectFieldOffset(field);
        this.field = field;
        name = field.getName();
        if (field.isAnnotationPresent(Rename.class))
        {
            Rename rename = field.getAnnotation(Rename.class);
            name = rename.value();
        }
        rootType = field.getType();
        while (rootType.isArray())
        {
            dimension++;
            rootType = rootType.getComponentType();
        }
        jsonKey = '"' + name + "\":";
    }
    
    /**
     * 将该field的值放入以json形式放入jsonobject之中 同时将该值放入collect中进行循环检测
     * 
     * @param host
     * @param jsonObject
     * @param collect
     */
    public void tranToJsonvalue(Object host, JsonObject jsonObject, ObjectCollect collect)
    {
        if (dimension == 0)
        {
            jsonObject.put(name, getSinlgeJsonValue(host, collect));
        }
        else
        {
            jsonObject.put(name, getJsonArray(dimension, unsafe.getObject(host, offset), collect));
        }
    }
    
    /**
     * 如果field是单一值（非数组），获取正确形式的值。比如string，integer，json等 同时将该值放入set中进行循环检测
     * 
     * @param host
     * @param collect
     * @return
     */
    protected abstract Object getSinlgeJsonValue(Object host, ObjectCollect collect);
    
    /**
     * 获取数组值value的jsonarray形式的值并且返回
     * 
     * @param dimension 当前数组的维度
     * @param value 数组
     * @param collect
     * @return
     */
    protected JsonArray getJsonArray(int dimension, Object value, ObjectCollect collect)
    {
        JsonArray jsonArray = new JsonArray();
        if (dimension > 1)
        {
            dimension--;
            for (Object each : (Object[]) value)
            {
                jsonArray.add(getJsonArray(dimension, each, collect));
            }
        }
        else
        {
            setOneDimension(value, jsonArray, collect);
        }
        return jsonArray;
    }
    
    /**
     * 当前数组value是一维数组，将其中的值以正确的形式放入到jsonarray中 同时将值放入set之中，进行循环检测
     * 
     * @param value
     * @param jsonArray
     * @param collect
     */
    public abstract void setOneDimension(Object value, JsonArray jsonArray, ObjectCollect collect);
    
    /**
     * 将该field的值以json格式写入字符串之中。同时需要将该值放入set中进行循环检测 如果该field的值为空，返回false。其他返回true
     * 
     * @param cache
     * @param host
     * @param collect
     * @return
     */
    public void writeToString(StringCache cache, Object host, ObjectCollect collect)
    {
        if (dimension == 0)
        {
            singleWriteToStr(cache, host, collect);
        }
        else
        {
            Object value = unsafe.getObject(host, offset);
            if (value != null)
            {
                cache.append(jsonKey);
                arrayWriteToStr(cache, value, collect, dimension);
                cache.append(',');
            }
        }
    }
    
    /**
     * 数组值value写入字符串之中。
     * 
     * @param cache
     * @param value 数组
     * @param collect
     * @param dimension 当前数组维度
     */
    private void arrayWriteToStr(StringCache cache, Object value, ObjectCollect collect, int dimension)
    {
        if (dimension > 1)
        {
            cache.append('[', 10);
            dimension--;
            for (Object each : (Object[]) value)
            {
                if (each != null)
                {
                    arrayWriteToStr(cache, each, collect, dimension);
                    cache.append(',');
                }
            }
            if (cache.isCommaLast())
            {
                cache.deleteLast();
            }
            cache.append(']');
        }
        else
        {
            writeToStringByOneDimension(cache, value, collect);
        }
    }
    
    /**
     * 将一维数组value写入字符串之后。并且数组中的值放入set中进行循环检测
     * 
     * @param cache
     * @param value
     * @param collect
     */
    protected abstract void writeToStringByOneDimension(StringCache cache, Object value, ObjectCollect collect);
    
    /**
     * field值不是数组，将值写入数组之中。值要放入set中进行循环检测
     * 
     * @param cache
     * @param value
     * @param collect
     */
    protected abstract void singleWriteToStr(StringCache cache, Object value, ObjectCollect collect);
    
    /**
     * 将jsonobject中对应的值进行转化放入对象之中
     * 
     * @param jsonObject
     * @param target
     */
    public void fillObject(JsonObject jsonObject, Object target)
    {
        if (dimension == 0)
        {
            fillSingle(jsonObject, target);
        }
        else
        {
            unsafe.putObject(target, offset, getRealArray(jsonObject.getJsonArray(name), dimension));
        }
    }
    
    /**
     * 如果该field不是数组，将jsonobject对象中的值进行合适的转化填入
     * 
     * @param jsonObject
     * @param target
     */
    protected abstract void fillSingle(JsonObject jsonObject, Object target);
    
    /**
     * 从jsonaray中还原出实际的数组对象
     * 
     * @param jsonArray
     * @param dimension
     * @return
     */
    protected Object getRealArray(JsonArray jsonArray, int dimension)
    {
        if (jsonArray == null)
        {
            return null;
        }
        if (dimension > 1)
        {
            int length = jsonArray.size();
            Object array = Array.newInstance(Array.newInstance(rootType, new int[dimension - 1]).getClass(), length);
            dimension--;
            for (int i = 0; i < length; i++)
            {
                Array.set(array, i, getRealArray((JsonArray) jsonArray.get(i), dimension));
            }
            return array;
        }
        else
        {
            return getOneDimensionArray(jsonArray);
        }
    }
    
    /**
     * 从jsonarray中得到实际的一维数组对象
     * 
     * @param jsonArray
     * @return
     */
    protected abstract Object getOneDimensionArray(JsonArray jsonArray);
}
